{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Client\n",
    "\n",
    "Demo of a client interacting with a remote agent that can use history.\n",
    "\n",
    "See relevant documentation about agents:\n",
    "\n",
    "* Creating a custom agent: https://python.langchain.com/docs/modules/agents/how_to/custom_agent\n",
    "* Streaming with agents: https://python.langchain.com/docs/modules/agents/how_to/streaming#custom-streaming-with-events\n",
    "* General streaming documentation: https://python.langchain.com/docs/expression_language/streaming"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can interact with this via API directly"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'output': {'output': 'The length of the word \"audioee\" is 7.'},\n",
       " 'callback_events': [],\n",
       " 'metadata': {'run_id': '1847be77-f53c-40ba-b88d-06af3a598b6e'}}"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import requests\n",
    "\n",
    "inputs = {\"input\": {\"input\": \"what is the length of the word audioee?\", \"chat_history\": []}}\n",
    "response = requests.post(\"http://localhost:8000/invoke\", json=inputs)\n",
    "\n",
    "response.json()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can also interact with this via the RemoteRunnable interface (to use in other chains)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "from langserve import RemoteRunnable\n",
    "\n",
    "remote_runnable = RemoteRunnable(\"http://localhost:8000/\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Remote runnable has the same interface as local runnables"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "from langchain_core.messages import HumanMessage, AIMessage"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "Human (Q/q to quit):  hello\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "AI: Hello! How can I assist you today?\n"
     ]
    },
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "Human (Q/q to quit):  my name is eugene\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "AI: Nice to meet you, Eugene! How can I help you today?\n"
     ]
    },
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "Human (Q/q to quit):  what is my name\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "AI: Your name is Eugene.\n"
     ]
    },
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "Human (Q/q to quit):  what is the length of my name\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "AI: The length of your name, Eugene, is 6 characters.\n"
     ]
    },
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "Human (Q/q to quit):  q\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "AI: Bye bye human\n"
     ]
    }
   ],
   "source": [
    "chat_history = []\n",
    "\n",
    "while True:\n",
    "    human = input(\"Human (Q/q to quit): \")\n",
    "    if human in {\"q\", \"Q\"}:\n",
    "        print('AI: Bye bye human')\n",
    "        break\n",
    "    ai = await remote_runnable.ainvoke({\"input\": human, \"chat_history\": chat_history})\n",
    "    print(f\"AI: {ai['output']}\")\n",
    "    chat_history.extend([HumanMessage(content=human), AIMessage(content=ai['output'])])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Stream\n",
    "\n",
    "Please note that streaming alternates between actions and observations. It does not stream individual tokens!\n",
    "\n",
    "To stream individual tokens, we need to use the astream events endpoint (see below)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "Human (Q/q to quit):  hello\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "AI: \n",
      "Hello! How can I assist you today?\n",
      "------\n"
     ]
    },
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "Human (Q/q to quit):  my name is eugene\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "AI: \n",
      "Nice to meet you, Eugene! How can I help you today?\n",
      "------\n"
     ]
    },
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "Human (Q/q to quit):  what is my name\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "AI: \n",
      "Your name is Eugene.\n",
      "------\n"
     ]
    },
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "Human (Q/q to quit):  what's the length of my name?\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "AI: \n",
      "Calling Tool ```word_length``` with input ```{'word': 'Eugene'}```\n",
      "------\n",
      "Got result: ```6```\n",
      "------\n",
      "The length of your name, Eugene, is 6 characters.\n",
      "------\n"
     ]
    },
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "Human (Q/q to quit):  q\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "AI: Bye bye human\n"
     ]
    }
   ],
   "source": [
    "chat_history = []\n",
    "\n",
    "while True:\n",
    "    human = input(\"Human (Q/q to quit): \")\n",
    "    if human in {\"q\", \"Q\"}:\n",
    "        print('AI: Bye bye human')\n",
    "        break\n",
    "\n",
    "    ai = None\n",
    "    print(\"AI: \")\n",
    "    async for chunk in remote_runnable.astream({\"input\": human, \"chat_history\": chat_history}):\n",
    "        # Agent Action\n",
    "        if \"actions\" in chunk:\n",
    "            for action in chunk[\"actions\"]:\n",
    "                print(\n",
    "                    f\"Calling Tool ```{action['tool']}``` with input ```{action['tool_input']}```\"\n",
    "                )\n",
    "        # Observation\n",
    "        elif \"steps\" in chunk:\n",
    "            for step in chunk[\"steps\"]:\n",
    "                print(f\"Got result: ```{step['observation']}```\")\n",
    "        # Final result\n",
    "        elif \"output\" in chunk:\n",
    "            print(chunk['output'])\n",
    "            ai = AIMessage(content=chunk['output'])\n",
    "        else:\n",
    "            raise ValueError\n",
    "        print(\"------\")        \n",
    "    chat_history.extend([HumanMessage(content=human), ai])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Stream Events"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "Human (Q/q to quit):  hello! my name is eugene\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "AI: \n",
      "Starting agent: agent with input: {'input': 'hello! my name is eugene', 'chat_history': []}\n",
      "Hello| Eugene|!| How| can| I| assist| you| today|?|\n",
      "--\n",
      "Done agent: agent with output: Hello Eugene! How can I assist you today?\n"
     ]
    },
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "Human (Q/q to quit):  what's the length of my name?\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "AI: \n",
      "Starting agent: agent with input: {'input': \"what's the length of my name?\", 'chat_history': []}\n",
      "--\n",
      "Starting tool: word_length with inputs: {'word': 'my name'}\n",
      "Done tool: word_length\n",
      "Tool output was: 7\n",
      "--\n",
      "The| length| of| your| name|,| \"|my| name|\",| is| |7| characters|.|\n",
      "--\n",
      "Done agent: agent with output: The length of your name, \"my name\", is 7 characters.\n"
     ]
    },
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "Human (Q/q to quit):  could you tell me a long story about the length of my name?\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "AI: \n",
      "Starting agent: agent with input: {'input': 'could you tell me a long story about the length of my name?', 'chat_history': []}\n",
      "Once| upon| a| time|,| there| was| a| person| named| [|Your| Name|].| Now|,| [|Your| Name|]| had| a| very| unique| and| special| name|.| It| was| a| name| that| carried| a| lot| of| meaning| and| significance|.| People| often| wondered| about| the| length| of| [|Your| Name|]'|s| name| and| how| it| compared| to| others|.\n",
      "\n",
      "|One| day|,| [|Your| Name|]| decided| to| embark| on| a| journey| to| discover| the| true| length| of| their| name|.| They| traveled| far| and| wide|,| seeking| the| wisdom| of| s|ages| and| scholars| who| were| known| for| their| knowledge| of| names| and| their| lengths|.\n",
      "\n",
      "|Along| the| way|,| [|Your| Name|]| encountered| many| interesting| characters| who| had| their| own| stories| to| tell| about| the| lengths| of| their| names|.| Some| had| short| names| that| were| easy| to| remember|,| while| others| had| long| names| that| seemed| to| go| on| forever|.\n",
      "\n",
      "|As| [|Your| Name|]| continued| their| quest|,| they| came| across| a| wise| old| wizard| who| claimed| to| have| a| magical| tool| that| could| calculate| the| exact| length| of| any| name|.| Intr|ig|ued|,| [|Your| Name|]| approached| the| wizard| and| asked| for| their| assistance|.\n",
      "\n",
      "|The| wizard| took| out| a| mystical| device| and| asked| [|Your| Name|]| to| spell| out| their| name|.| [|Your| Name|]| eagerly| complied|,| carefully| en|unci|ating| each| letter|.| The| wizard| then| waved| the| device| over| the| name| and| muttered| an| inc|ant|ation|.\n",
      "\n",
      "|In| an| instant|,| the| device| displayed| the| length| of| [|Your| Name|]'|s| name|.| It| was| a| number| that| represented| the| total| count| of| characters| in| their| name|,| including| spaces| and| punctuation| marks|.| [|Your| Name|]| was| amazed| to| see| the| result| and| thanked| the| wizard| for| their| help|.\n",
      "\n",
      "|Ar|med| with| this| newfound| knowledge|,| [|Your| Name|]| returned| home| and| shared| their| story| with| friends| and| family|.| They| realized| that| the| length| of| their| name| was| not| just| a| random| number|,| but| a| reflection| of| their| identity| and| the| unique| journey| they| had| taken| to| discover| it|.\n",
      "\n",
      "|And| so|,| [|Your| Name|]| lived| happily| ever| after|,| cher|ishing| their| name| and| the| story| behind| its| length|.| They| understood| that| the| length| of| a| name| is| not| just| a| matter| of| counting| letters|,| but| a| testament| to| the| individual|ity| and| significance| of| each| person|'s| identity|.|\n",
      "--\n",
      "Done agent: agent with output: Once upon a time, there was a person named [Your Name]. Now, [Your Name] had a very unique and special name. It was a name that carried a lot of meaning and significance. People often wondered about the length of [Your Name]'s name and how it compared to others.\n",
      "\n",
      "One day, [Your Name] decided to embark on a journey to discover the true length of their name. They traveled far and wide, seeking the wisdom of sages and scholars who were known for their knowledge of names and their lengths.\n",
      "\n",
      "Along the way, [Your Name] encountered many interesting characters who had their own stories to tell about the lengths of their names. Some had short names that were easy to remember, while others had long names that seemed to go on forever.\n",
      "\n",
      "As [Your Name] continued their quest, they came across a wise old wizard who claimed to have a magical tool that could calculate the exact length of any name. Intrigued, [Your Name] approached the wizard and asked for their assistance.\n",
      "\n",
      "The wizard took out a mystical device and asked [Your Name] to spell out their name. [Your Name] eagerly complied, carefully enunciating each letter. The wizard then waved the device over the name and muttered an incantation.\n",
      "\n",
      "In an instant, the device displayed the length of [Your Name]'s name. It was a number that represented the total count of characters in their name, including spaces and punctuation marks. [Your Name] was amazed to see the result and thanked the wizard for their help.\n",
      "\n",
      "Armed with this newfound knowledge, [Your Name] returned home and shared their story with friends and family. They realized that the length of their name was not just a random number, but a reflection of their identity and the unique journey they had taken to discover it.\n",
      "\n",
      "And so, [Your Name] lived happily ever after, cherishing their name and the story behind its length. They understood that the length of a name is not just a matter of counting letters, but a testament to the individuality and significance of each person's identity.\n"
     ]
    },
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "Human (Q/q to quit):  that's not what I wanted. My name is eugene. calculate the length of my name and tell me a story about the result.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "AI: \n",
      "Starting agent: agent with input: {'input': \"that's not what I wanted. My name is eugene. calculate the length of my name and tell me a story about the result.\", 'chat_history': []}\n",
      "--\n",
      "Starting tool: word_length with inputs: {'word': 'eugene'}\n",
      "Done tool: word_length\n",
      "Tool output was: 6\n",
      "--\n",
      "The| length| of| your| name|,| Eugene|,| is| |6| characters|.| Now|,| let| me| tell| you| a| story| about| the| result|.\n",
      "\n",
      "|Once| upon| a| time|,| in| a| land| far| away|,| there| was| a| young| boy| named| Eugene|.| He| had| a| special| power| -| the| power| to| bring| joy| and| laughter| to| everyone| he| met|.| Eugene|'s| name|,| with| its| |6| letters|,| perfectly| reflected| his| vibrant| and| energetic| personality|.\n",
      "\n",
      "|One| day|,| Eugene| decided| to| embark| on| a| grand| adventure|.| He| set| off| on| a| journey| to| spread| happiness| and| positivity| throughout| the| kingdom|.| Along| the| way|,| he| encountered| people| from| all| walks| of| life| -| from| humble| farmers| to| noble| knights|.\n",
      "\n",
      "|With| his| infectious| smile| and| kind| heart|,| Eugene| touched| the| lives| of| everyone| he| met|.| His| name|,| with| its| |6| letters|,| became| synonymous| with| love|,| compassion|,| and| joy|.| People| would| often| say|,| \"|If| you| want| to| experience| true| happiness|,| just| spend| a| moment| with| Eugene|.\"\n",
      "\n",
      "|As| Eugene| continued| his| journey|,| word| of| his| incredible| ability| to| bring| happiness| spread| far| and| wide|.| People| from| distant| lands| would| travel| for| miles| just| to| catch| a| glimpse| of| him|.| His| name|,| with| its| |6| letters|,| became| a| symbol| of| hope| and| inspiration|.\n",
      "\n",
      "|E|ug|ene|'s| story| serves| as| a| reminder| that| sometimes|,| the| simplest| things| can| have| the| greatest| impact|.| His| name|,| with| its| |6| letters|,| became| a| beacon| of| light| in| a| world| that| often| seemed| dark| and| glo|omy|.\n",
      "\n",
      "|And| so|,| Eugene|'s| adventure| continues|,| as| he| spreads| joy| and| happiness| wherever| he| goes|.| His| name|,| with| its| |6| letters|,| will| forever| be| et|ched| in| the| hearts| of| those| whose| lives| he| has| touched|.\n",
      "\n",
      "|The| end|.|\n",
      "--\n",
      "Done agent: agent with output: The length of your name, Eugene, is 6 characters. Now, let me tell you a story about the result.\n",
      "\n",
      "Once upon a time, in a land far away, there was a young boy named Eugene. He had a special power - the power to bring joy and laughter to everyone he met. Eugene's name, with its 6 letters, perfectly reflected his vibrant and energetic personality.\n",
      "\n",
      "One day, Eugene decided to embark on a grand adventure. He set off on a journey to spread happiness and positivity throughout the kingdom. Along the way, he encountered people from all walks of life - from humble farmers to noble knights.\n",
      "\n",
      "With his infectious smile and kind heart, Eugene touched the lives of everyone he met. His name, with its 6 letters, became synonymous with love, compassion, and joy. People would often say, \"If you want to experience true happiness, just spend a moment with Eugene.\"\n",
      "\n",
      "As Eugene continued his journey, word of his incredible ability to bring happiness spread far and wide. People from distant lands would travel for miles just to catch a glimpse of him. His name, with its 6 letters, became a symbol of hope and inspiration.\n",
      "\n",
      "Eugene's story serves as a reminder that sometimes, the simplest things can have the greatest impact. His name, with its 6 letters, became a beacon of light in a world that often seemed dark and gloomy.\n",
      "\n",
      "And so, Eugene's adventure continues, as he spreads joy and happiness wherever he goes. His name, with its 6 letters, will forever be etched in the hearts of those whose lives he has touched.\n",
      "\n",
      "The end.\n"
     ]
    },
    {
     "name": "stdin",
     "output_type": "stream",
     "text": [
      "Human (Q/q to quit):  q\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "AI: Bye bye human\n"
     ]
    }
   ],
   "source": [
    "chat_history = []\n",
    "\n",
    "while True:\n",
    "    human = input(\"Human (Q/q to quit): \")\n",
    "    if human in {\"q\", \"Q\"}:\n",
    "        print('AI: Bye bye human')\n",
    "        break\n",
    "    ai = None\n",
    "    print(\"AI: \")\n",
    "    async for event in remote_runnable.astream_events(\n",
    "        {\"input\": human, \"chat_history\": chat_history},\n",
    "        version=\"v1\",\n",
    "    ):\n",
    "        kind = event[\"event\"]\n",
    "        if kind == \"on_chain_start\":\n",
    "            if (\n",
    "                event[\"name\"] == \"agent\"\n",
    "            ):  # Was assigned when creating the agent with `.with_config({\"run_name\": \"Agent\"})`\n",
    "                print(\n",
    "                    f\"Starting agent: {event['name']} with input: {event['data'].get('input')}\"\n",
    "                )\n",
    "        elif kind == \"on_chain_end\":\n",
    "            if (\n",
    "                event[\"name\"] == \"agent\"\n",
    "            ):  # Was assigned when creating the agent with `.with_config({\"run_name\": \"Agent\"})`\n",
    "                print()\n",
    "                print(\"--\")\n",
    "                print(\n",
    "                    f\"Done agent: {event['name']} with output: {event['data'].get('output')['output']}\"\n",
    "                )\n",
    "        if kind == \"on_chat_model_stream\":\n",
    "            content = event[\"data\"][\"chunk\"].content\n",
    "            if content:\n",
    "                # Empty content in the context of OpenAI means\n",
    "                # that the model is asking for a tool to be invoked.\n",
    "                # So we only print non-empty content\n",
    "                print(content, end=\"|\")\n",
    "        elif kind == \"on_tool_start\":\n",
    "            print(\"--\")\n",
    "            print(\n",
    "                f\"Starting tool: {event['name']} with inputs: {event['data'].get('input')}\"\n",
    "            )\n",
    "        elif kind == \"on_tool_end\":\n",
    "            print(f\"Done tool: {event['name']}\")\n",
    "            print(f\"Tool output was: {event['data'].get('output')}\")\n",
    "            print(\"--\")    \n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
